package cn.getech.data.development.job.util

import scala.util.control.Breaks.{break, breakable}
import com.alibaba.fastjson.{JSON, JSONArray, JSONObject}
import com.sun.jersey.api.client.Client
import com.sun.jersey.api.client.ClientResponse
import com.sun.jersey.api.client.WebResource
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter

import scala.collection.mutable
import scala.collection.mutable.ListBuffer


object RangerRestUtil {
  def main(args: Array[String]): Unit = {
    /*val url = "http://bigdata-test-4:6080/service/plugins/policies/service/1?page=0&pageSize=25&total_pages=37&totalCount=919&startIndex=0&policyType=0&user=user_7&resource%3Adatabase=ods&resource%3Atable=ods_jthr_z05_get_pernr_t0000"
    val url2 = "http://bigdata-test-4:6080/service/plugins/policies/service/1?user=user_7&resource%3Adatabase=ods&resource%3Atable=ods_jthr_z05_get_pernr_t0000"
    val url1 = "http://bigdata-test-4:6080/service/xusers/groups?page=0&pageSize=25&total_pages=2&totalCount=29&sortBy=id&sortType=asc&startIndex=0&name=explore_user&_=1585377107613"                                                                                                                  //ods_jthr_z05_get_pernr_t0000
    val urlGroup = "http://bigdata-test-4:6080/service/plugins/policies/service/1?page=0&pageSize=25&total_pages=58&totalCount=1442&startIndex=0&policyType=0&group=explore_user&_=1585377107620"
    val EXPECTED_MIME_TYPE = "application/json"
    var client: Client = null
    var response: ClientResponse = null
    try {
      client = Client.create()
      client.addFilter(new HTTPBasicAuthFilter("admin", "admin"))
      val webResource: WebResource = client.resource(url2)
      response = webResource.accept(EXPECTED_MIME_TYPE).get(classOf[ClientResponse])
      if(response.getStatus() == 200) {
        val  jsonString: String = response.getEntity(classOf[String])
        println(jsonString)
      }
    } finally {
      if(response != null) {
        response.close()
      }
      if(client != null) {
        client.destroy()
      }
    }*/
    val util = new RangerRestUtil("http://bigdata-test-4:6080/")
    val result: Boolean = util.queryPermissionByUserAndTable("user_78","ods","ods_jthr_z05_get_pernr_t9018")
    println(result)
  }
}

class RangerRestUtil(rangerUrl:String){

  private val rangerURL = rangerUrl
  private val EXPECTED_MIME_TYPE = "application/json"
  var client: Client = null
  var webResource: WebResource = null

  def init(): Unit ={
     client = Client.create()
     client.addFilter(new HTTPBasicAuthFilter("admin", "admin"))
  }

  def queryPermissionByUserAndTables(userName: String, dbName: String, tableNames: Seq[String]): mutable.Map[String,Boolean] = {
    val getSB = new StringBuilder(rangerURL)
    var response: ClientResponse = null
    var responseStr: String = ""
    var result:Boolean = false
    val map: mutable.Map[String, Boolean] = mutable.Map[String,Boolean]()

    init()

    for (tableName <- tableNames) {
      if(rangerURL.endsWith("/")){
        getSB.append("service/plugins/policies/service/1?user=").append(userName)
          .append("&resource%3Adatabase=").append(dbName)
          .append("&resource%3Atable=").append(tableName)
      }else{
        getSB.append("/service/plugins/policies/service/1?user=").append(userName)
          .append("&resource%3Adatabase=").append(dbName)
          .append("&resource%3Atable=").append(tableName)
      }

      //println(s" - - - requestUrl : ${getSB} - - -")
      webResource = client.resource(getSB.toString())
      response = webResource.accept(EXPECTED_MIME_TYPE).get(classOf[ClientResponse])
      if(response.getStatus() == 200) {
        responseStr = response.getEntity(classOf[String])
        val responsejObj: JSONObject = JSON.parseObject(responseStr)
        //检验策略
        result = checkPolicyCorrectness(responsejObj,userName,dbName,tableName)
        map.put(tableName,result)
      }
    }

    //关闭资源
    if(null != response){
      response.close()
    }

    if(null != client){
      client.destroy()
    }

    map
  }



  def queryPermissionByUserAndTable(userName: String, dbName: String, tableName: String): Boolean ={

    val getSB = new StringBuilder(rangerURL)
    var responseStr: String = ""
    var result:Boolean = false

    init()

    if(rangerURL.endsWith("/")){
      getSB.append("service/plugins/policies/service/1?user=").append(userName)
        .append("&resource%3Adatabase=").append(dbName)
        .append("&resource%3Atable=").append(tableName)
    }else{
      getSB.append("/service/plugins/policies/service/1?user=").append(userName)
        .append("&resource%3Adatabase=").append(dbName)
        .append("&resource%3Atable=").append(tableName)
    }

    //println(s" - - - requestUrl : ${getSB} - - -")
    webResource = client.resource(getSB.toString())
    val response: ClientResponse = webResource.accept(EXPECTED_MIME_TYPE).get(classOf[ClientResponse])
    if(response.getStatus() == 200) {
      responseStr = response.getEntity(classOf[String])
      val responsejObj: JSONObject = JSON.parseObject(responseStr)
      //检验策略
      result = checkPolicyCorrectness(responsejObj,userName,dbName,tableName)
    }

    if(null != response){
      response.close()
    }

    if(null != client){
      client.destroy()
    }

    result
  }

  /**
   * 检查用户组包含的策略
   * @param usersGroups 某用户包含的用户组
   * @param userName 某用户
   * @param dbName 目标数据库
   * @param tableName 目标表
   * @return boolean 用户所在组策略是否包含目标表
   */
  def checkGroups(usersGroups: JSONArray,userName:String,dbName:String,tableName:String): Boolean = {
    var dbCheck = false
    var tableCheck = false
    var updateCheck = false
    var checkResult = false

    //遍历获取group相关的公共策略
    for(userGroupIndex <-  0 until usersGroups.size()){

      //get url
      val builder = new StringBuilder("http://bigdata-test-4:6080/service/plugins/policies/service/1?group=")
      builder.append(usersGroups.getString(userGroupIndex))

      //请求策略
      val resource: WebResource = client.resource(builder.toString())
      val response: ClientResponse = resource.accept(EXPECTED_MIME_TYPE).get(classOf[ClientResponse])

      //结果解析
      if(response.getStatus() == 200) {
        val responseStr = response.getEntity(classOf[String])
        val responsejObj: JSONObject = JSON.parseObject(responseStr)
        val policiesArr: JSONArray = responsejObj.getJSONArray("policies")

        for(policiesIndex <- 0 until policiesArr.size()){
          val resources: JSONObject = policiesArr.getJSONObject(policiesIndex).getJSONObject("resources")
          val policyItems: JSONArray = policiesArr.getJSONObject(policiesIndex).getJSONArray("policyItems")

          val dbArray: JSONArray = resources.getJSONObject("database").getJSONArray("values")
          val dbIsExcludes = resources.getJSONObject("database").getBoolean("isExcludes")

          val tableArray: JSONArray = resources.getJSONObject("table").getJSONArray("values")
          val tableIsExcludes = resources.getJSONObject("table").getBoolean("isExcludes")

          //数据库匹配检查
          if(dbArray.contains("*") || dbArray.contains(dbName)){
            if(!dbIsExcludes){
              dbCheck = true
            }
          }

          //表匹配检查
          if(tableArray.contains("*") || tableArray.contains(tableName)){
            if(!tableIsExcludes){
              tableCheck = true
            }
          }

          //权限类型检查
          for(policyItemIndex <- 0 until policyItems.size()){
            val usersArray: JSONArray = policyItems.getJSONObject(policyItemIndex).getJSONArray("users")
            val accessesArray: JSONArray = policyItems.getJSONObject(policyItemIndex).getJSONArray("accesses")
            if(usersArray.contains(userName) || usersArray.contains(s"{USER}") || usersArray.contains("*")){
              if(accessesArray.contains(JSON.parseObject("{\"type\":\"update\",\"isAllowed\":true}"))){
                updateCheck = true
              }
            }
          }
        }
      }
    }

    //都符合则有权限
    if(dbCheck && tableCheck && updateCheck){
      checkResult = true
    }

    checkResult
  }

  /**
   * 检查策略
   * @param responseJobj 请求jsonObj
   * @param userName ranger用户名
   * @param dbName 目标数据库
   * @param tableName 目标表
   * @return boolean
   */
  def checkPolicyCorrectness(responseJobj: JSONObject, userName: String, dbName: String, tableName: String): Boolean ={

    var flag = false
    val policiesArr: JSONArray = responseJobj.getJSONArray("policies")

    for(arrIndex <- 0 until policiesArr.size()){
      val resourceObj: JSONObject = policiesArr.getJSONObject(arrIndex).getJSONObject("resources")
      val policyItems: JSONArray = policiesArr.getJSONObject(arrIndex).getJSONArray("policyItems")
      val database: JSONObject = resourceObj.getJSONObject("database")
      val table: JSONObject = resourceObj.getJSONObject("table")
      var userCheck = false
      var usersGroups: JSONArray = new JSONArray()


      for(policyItemsSize <- 0 until policyItems.size()){
        val policyItem: JSONObject = policyItems.getJSONObject(policyItemsSize)
        val users: JSONArray = policyItem.getJSONArray("users")
        val groups: JSONArray = policyItem.getJSONArray("groups")
        if(users.contains(userName)){
          userCheck = true
          usersGroups = groups
        }
      }

      //验证数据库和是排除还是包含
      if(! database.getJSONArray("values").contains(dbName) || database.getBoolean("isExcludes")) flag = false

      //验证数据库和是排除还是包含
      if(! table.getJSONArray("values").contains(tableName) || table.getBoolean("isExcludes")) flag = false

      //验证数据库和是排除还是包含
      if(! database.getJSONArray("values").contains(dbName) || database.getBoolean("isExcludes")) flag = false

      //通过库，表，用户后 验证权限
      val policyItemsArr: JSONArray = policiesArr.getJSONObject(arrIndex).getJSONArray("policyItems")
      for(policyItemsIndex <- 0 until policyItemsArr.size()){
        val policyItemObj: JSONObject = policyItemsArr.getJSONObject(policyItemsIndex)
        val userArr: JSONArray = policyItemObj.getJSONArray("users")
        //校验 用户
        if(userArr.contains(userName)){
          //校验 update 权限
          if(policyItemObj.getJSONArray("accesses").contains(JSON.parseObject("{\"type\":\"update\",\"isAllowed\":true}"))){
            flag = true
            return flag
          }
        }
      }

      if(usersGroups.size() > 0 && !flag) {
        flag = checkGroups(usersGroups,userName,dbName,tableName)
      }
    }

    flag
  }
}
